use anyhow::{Result, Context};
use md5::{Md5, Digest};
use rayon::prelude::*;
use std::fs::File;
use std::io::{Read, BufReader};
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use walkdir::WalkDir;

use crate::cli::{parse_size, default_worker_count};
use crate::db;
use std::num::NonZeroUsize;

/// 计算文件的MD5值
fn calculate_md5(file_path: &Path) -> Result<String> {
    let file = File::open(file_path).context(format!("无法打开文件: {:?}", file_path))?;
    let mut reader = BufReader::new(file);
    let mut hasher = Md5::new();
    let mut buffer = [0; 8192]; // 8KB 缓冲区，提高读取效率

    loop {
        let bytes_read = reader.read(&mut buffer)?;
        if bytes_read == 0 {
            break;
        }
        hasher.update(&buffer[..bytes_read]);
    }

    let result = hasher.finalize();
    Ok(format!("{:x}", result))
}

/// 扫描目录并计算文件的MD5值
pub fn scan_directory(
    directory: PathBuf,
    min_size_str: String,
    workers: Option<NonZeroUsize>
) -> Result<()> {
    // 解析最小文件大小
    let min_size = parse_size(&min_size_str)
        .map_err(|e| anyhow::anyhow!("解析大小参数错误: {}", e))?;

    // 确定工作线程数，默认为CPU核心数的1.5倍
    let worker_count = workers.unwrap_or_else(default_worker_count);

    // 配置线程池
    rayon::ThreadPoolBuilder::new()
        .num_threads(worker_count.get())
        .build_global()
        .context("无法配置线程池")?;

    println!("扫描目录: {:?}", directory);
    println!("最小文件大小: {} 字节", min_size);
    println!("工作线程数: {}", worker_count);

    // 首先收集所有符合条件的文件
    let mut files = Vec::new();

    // 使用 WalkDir 递归遍历目录中的所有文件
    for entry in WalkDir::new(&directory)
        .follow_links(false)  // 不跟踪符号链接
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| e.file_type().is_file())  // 只处理文件，不处理目录
    {
        if let Ok(metadata) = entry.metadata() {
            let file_size = metadata.len();

            // 应用最小文件大小筛选
            if file_size >= min_size {
                files.push(entry.path().to_path_buf());
            }
        }
    }

    let total_files = files.len();
    println!("找到符合条件的文件: {} 个", total_files);

    if total_files == 0 {
        return Ok(());
    }

    // 文件计数器，用于显示进度
    let counter = Arc::new(AtomicUsize::new(0));

    // 并行处理文件，使用 rayon 并行迭代器提高性能
    files.par_iter().for_each(|file_path| {
        let current = counter.fetch_add(1, Ordering::SeqCst) + 1;
        let path_str = file_path.to_string_lossy().to_string();

        // 检查文件是否已在数据库中，如果存在则跳过计算 MD5
        match db::is_file_in_db(&path_str) {
            Ok(Some(_)) => {
                // 文件已在数据库中，跳过处理
                println!("[{}/{}] [Skip] {}", current, total_files, path_str);
            },
            _ => {
                // 计算文件大小
                if let Ok(metadata) = std::fs::metadata(file_path) {
                    let file_size = metadata.len() as i64;

                    // 计算MD5值
                    match calculate_md5(file_path) {
                        Ok(md5_hash) => {
                            // 保存到数据库
                            if let Err(e) = db::save_file(&path_str, &md5_hash, file_size) {
                                eprintln!("保存文件记录失败: {}", e);
                            }

                            println!("[{}/{}] {}", current, total_files, path_str);
                        },
                        Err(e) => {
                            eprintln!("计算MD5失败: {} - 错误: {}", path_str, e);
                            println!("[{}/{}] [Skip] {}", current, total_files, path_str);
                        }
                    }
                } else {
                    println!("[{}/{}] [Skip] {}", current, total_files, path_str);
                }
            }
        }
    });

    Ok(())
}

/// 检查数据库中的文件是否存在
pub fn check_files() -> Result<()> {
    // 获取所有文件记录
    let files = db::get_all_files().context("获取文件记录失败")?;

    let total_files = files.len();
    println!("数据库中共有 {} 个文件记录", total_files);

    if total_files == 0 {
        return Ok(());
    }

    // 文件计数器
    let counter = Arc::new(AtomicUsize::new(0));

    // 并行检查文件是否存在
    files.par_iter().for_each(|file| {
        let current = counter.fetch_add(1, Ordering::SeqCst) + 1;
        let path = Path::new(&file.file_path);

        if path.exists() {
            // 文件存在，保留记录
            println!("[N] [{}/{}] {}", current, total_files, file.file_path);
        } else {
            // 文件不存在，删除记录
            if let Err(e) = db::delete_file(&file.file_path) {
                eprintln!("删除文件记录失败: {}", e);
            }
            println!("[D] [{}/{}] {}", current, total_files, file.file_path);
        }
    });

    Ok(())
}

/// 查询MD5相同的文件并按文件从大到小排序
pub fn query_duplicate_files() -> Result<()> {
    // 获取具有重复MD5值的文件组
    let duplicate_groups = db::get_duplicate_files().context("获取重复文件记录失败")?;

    if duplicate_groups.is_empty() {
        println!("没有找到MD5相同的文件");
        return Ok(());
    }

    // 将所有重复文件收集到一个向量中
    let mut all_duplicate_files = Vec::new();
    for group in duplicate_groups {
        for file in group {
            all_duplicate_files.push(file);
        }
    }

    // 按文件大小从大到小排序
    all_duplicate_files.sort_by(|a, b| b.file_size.cmp(&a.file_size));

    // 按照要求的格式输出：file_path|file_md5|file_size
    for file in all_duplicate_files {
        println!("{}|{}|{}", file.file_path, file.file_md5, file.file_size);
    }

    Ok(())
}
